#include "Components/ComponentHealth.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentSleep.h"
#include "Components/ComponentVitalStats.h"
#include "Components/ComponentCreatureModel.h"
#include "Components/ComponentClothing.h"
#include "Components/ComponentCraftingTable.h"
#include "Components/ComponentInventory.h"
#include "Components/ComponentOnFire.h"
#include "Components/ComponentLevel.h"
#include "Components/ComponentSpawn.h"



#include "Log.h"
#include "Network/PackageManager.h"
#include "Network/Packages/ComponentHealthPackage.h"
#include "Network/Packages/MessagePackage.h"

#include "Player/PlayerData.h"

#include "Engine/Random.hpp"
#include "Subsystems/SubsystemPickables.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Managers/BlocksManager.h"

#include "Blocks/MagmaBlock.h"
#include "Blocks/ExperienceBlock.h"
#include "Game/TableComponent.hpp"

#include "GameRandom.hpp"
#include "GameRegistry.hpp"
#include "ServerConfigure.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"
#include "Expand/EventManager.h"

namespace PocketSurvival
{
    void ComponentHealth::addAttackedEvent(std::function<void(entt::entity)> event)
    {
        m_attackedEvents += event;
    }

    void ComponentHealth::heal(float amount)
    {
        if(amount > 0.0f)
        {
            m_healthLock.lock();
            health = MathUtils::Min<float>(health + amount, 1.0f);
            m_healthLock.unlock();
        }
    }

    void ComponentHealth::injure(float amount, entt::entity attackerEntity, bool realInjure, const std::string &cause, InjureType injureType)
    {
        // 伤害没有大于0的话，下面的操作也就没有必要了
        // 这里要注意，真实伤害的话，就算是创造模式也会受到伤害
        if (amount <= 0.0f || (componentPlayer != nullptr && componentPlayer->playerData->gameMode == GameMode::Creative && realInjure == false))
        {
            return;
        }
        // InjureType::Plugin 导致的伤害时间，将不会被其它插件所触发
        if (injureType != InjureType::Plugin &&
            (GameSingleton::eventManager->creatureEvent->injureEntity(this, amount, attackerEntity, realInjure, cause, injureType) == false || invincibleTime > 0))
        {
            return;
        }
        netInjure(amount, attackerEntity, cause);
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentHealthPackage>(this, attackerEntity, amount, cause));
    }

    void ComponentHealth::netInjure(float amount, entt::entity attackerEntity, const std::string &cause)
    {
        bool nowDeath = false;

        m_healthLock.lock();
        if (health > 0.0f)
        {
            health = MathUtils::Saturate(health);
            health = MathUtils::Max<float>(health - amount, 0.0f);
            if (health == 0)
            {
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentHealthPackage>(this, attackerEntity, 1.0f, cause));
                nowDeath = true;
            }
        }
        m_healthLock.unlock();

        if (nowDeath == true)
        {
            deathTime = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>()->getGameTime();
            if (GameSingleton::eventManager->creatureEvent->entityDeath(this, amount, attackerEntity, cause) == false)
            {
                return;
            }

            if (attackerEntity != entt::null)
            {
                // 如果攻击者是玩家，那么击杀实体后就会掉经验
                ComponentPlayer **componentPlayerPtr = GameSingleton::gameRegistry->try_get<ComponentPlayer *>(attackerEntity);
                if (componentPlayerPtr != nullptr && entity != attackerEntity)
                {
                    ComponentPlayer *componentPlayer = *componentPlayerPtr;
                    int32_t num = (int32_t)MathUtils::Ceil<float>(attackResilience / 12.0f);

                    Vector3 position;
                    componentCreature->componentBody->getPosition(position);

                    for (int32_t index = 0; index < num; ++index)
                    {
                        Vector2 vector2 = GameSingleton::gameRandom->getVector2(2.5f, 3.5f);
                        GameSingleton::singletonPtr->getSingleton<SubsystemPickables>()->addPickable(ExperienceBlock::Index(), 1, position, std::make_shared<Vector3>(vector2.posX, 6.0f, vector2.posY), nullptr);
                    }
                }
            }

            // 非玩家或者是没有开启死亡不掉落的时候，要清空背包物品
            if (componentPlayer == nullptr || GameSingleton::serverConfigure->deadNotDrop == false)
            {
                if (GameSingleton::eventManager->creatureEvent->dropAllItems(entity) == true)
                {
                    Vector3 position;
                    componentCreature->componentBody->getPosition(position);

                    TableComponent *tableComponent = GameSingleton::gameRegistry->get<TableComponent *>(this->entity);
                    std::vector<IInventory *> inventoryVec;
                    tableComponent->dynamicFindComponent<IInventory>(inventoryVec);
                    for (IInventory *inventory : inventoryVec)
                    {
                        inventory->dropAllItems(position);
                    }
                }


                // // 清空背包
                // ComponentInventory **componentInventoryPtr = GameSingleton::gameRegistry->try_get<ComponentInventory *>(this->entity);
                // if (componentInventoryPtr != nullptr)
                // {
                //     (*componentInventoryPtr)->dropAllItems(componentCreature->componentBody->position);
                // }
                // // 清空工作台
                // ComponentCraftingTable **componentCraftingTablePtr = GameSingleton::gameRegistry->try_get<ComponentCraftingTable *>(this->entity);
                // if (componentCraftingTablePtr != nullptr)
                // {
                //     (*componentCraftingTablePtr)->dropAllItems(componentCreature->componentBody->position);
                // }
                // // 清空衣服
                // ComponentClothing **componentClothingPtr = GameSingleton::gameRegistry->try_get<ComponentClothing *>(this->entity);
                // if (componentClothingPtr != nullptr)
                // {
                //     (*componentClothingPtr)->dropAllItems(componentCreature->componentBody->position);
                // }
            }

            if (componentPlayer != nullptr)
            {
                std::string message = fmt::format("{} <c=red>{}</c>", componentPlayer->playerData->username, cause);
                Log::Info(message, Log::LogType::PLAYER);
                GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<MessagePackage>(message));
            }
        }

        m_attackedEvents.invoke(attackerEntity);
    }

    void ComponentHealth::update(float dt)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        SubsystemTerrain *subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        ComponentBody *componentBody = componentCreature->componentBody;

        Vector3 position;
        componentBody->getPosition(position);

        invincibleTime = invincibleTime - dt;
        if (invincibleTime < 0)
        {
            invincibleTime = 0;
        }

        if (timeOfDay->periodicGameTimeEvent(2.0, 0.0))
        {
            GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentHealthPackage>(this));
        }
        if (health > 0.0f && health < 1.0f)
        {
            float num = 0.0f;
            if (componentPlayer != nullptr)
            {
                if (componentPlayer->playerData->gameMode == GameMode::Harmless)
                {
                    num += 0.01f;
                }
                // 这个跟原版不一样，吃饱了应该算是有而外的恢复加成
                if (componentPlayer->componentSleep->isSleeping() && componentPlayer->componentVitalStats->food > 0.8f)
                {
                    num += 0.0016f;
                }
                else if (componentPlayer->componentVitalStats->food > 0.35f)
                {
                    num += 0.0011111f;
                }
            }
            else
            {
                num = 0.0001f;
            }
            heal(dt * num);
        }

        // 计算氧气值
        if (breathingMode == BreathingMode::Air)
        {
            int32_t cellContents = subsystemTerrain->getCellContents(Terrain::ToCell(position.posX), Terrain::ToCell(componentCreature->componentCreatureModel->getEyePosition().posY), Terrain::ToCell(position.posZ));
            bool isFluidBlock = (GameSingleton::blocksManager->blocks[cellContents]->isBlock<FluidBlock *>() == true);
            if (isFluidBlock || position.posY > 259.0f)
            {
                air = MathUtils::Saturate(air - dt / airCapacity);
            }
            else
            {
                // 直接恢复氧气值
                air = 1.0f;
                // 缓慢恢复氧气
                // air = MathUtils::Min(air + dt / airCapacity * 1.5f, 1.0f);
            }
        }
        else if (breathingMode == BreathingMode::Water)
        {
            componentBody->immersionFactor > 0.25f ? 1.0f : MathUtils::Saturate(air - dt / airCapacity);
        }

        // 岩浆伤害
        if (componentBody->immersionFactor > 0.0f && componentBody->immersionFluidBlock->isBlock<MagmaBlock *>() == true)
        {
            injure(2.0f * componentBody->immersionFactor * dt, entt::null, false, "试图才岩浆里面游泳", InjureType::Fire);
        }

        // 掉落伤害
        float num = MathUtils::Abs(componentCreature->componentBody->collisionVelocityChange.posY);
        if(num > fallResilience)
        {
            float amount = MathUtils::Sqr<float>(MathUtils::Max<float>(num - fallResilience, 0.0f)) / 25.0f;
            if(componentPlayer != nullptr)
            {
                amount /= componentPlayer->componentLevel->getResilienceFactor();
            }
            injure(amount, entt::null, false, "摔死了", InjureType::Fall);
        }

        if (timeOfDay->periodicGameTimeEvent(1.0f, 0.0f))
        {
            // 超出世界的限制
            if (position.posY < 0.0f)
            {
                injure(0.05f, entt::null, true, "掉出世界");
            }
            // 窒息伤害
            if (air == 0.0f)
            {
                float num6 = 0.12f;
                if (componentPlayer != nullptr)
                {
                    num6 /= componentPlayer->componentLevel->getResilienceFactor();
                }
                injure(num6, entt::null, false, "窒息而死");
            }
            // 着火伤害
            if (componentOnFire->fireDuration > 0.0f || componentOnFire->touchesFire)
            {
                float num7 = 1.0f / fireResilience;
                if (componentPlayer != nullptr)
                {
                    num7 /= componentPlayer->componentLevel->getResilienceFactor();
                }
                std::string fireCause = "被火烧死了";
                if (componentOnFire->attacker != entt::null)
                {
                    ComponentPlayer **attackerPlayer = GameSingleton::gameRegistry->try_get<ComponentPlayer *>(componentOnFire->attacker);
                    if (attackerPlayer != nullptr && (*attackerPlayer)->playerData != nullptr)
                    {
                        fireCause = fmt::format("被 {} 放火烧死了", (*attackerPlayer)->playerData->username);
                    }
                }
                injure(num7, componentOnFire->attacker, false, fireCause);
            }
            // 搁浅伤害
            if (canStrand && componentBody->immersionFactor < 0.25f && (componentBody->standingOnValue != 0 || componentBody->standingOnBody != nullptr))
            {
                injure(0.05f, entt::null, false, "搁浅");
            }
        }
        healthChange = health - lastHealth;
        lastHealth = health;

        if (health <= 0.0f && corpseDuration > 0.0f && timeOfDay->getGameTime() - deathTime > corpseDuration)
        {
            if (componentPlayer == nullptr)
            {
                componentCreature->componentSpawn->despawn();
            }
        }

        // 其它伤害， 未完成...
    }

    static const std::string componentName = "Health";
    const std::string &ComponentHealth::getName() const
    {
        return componentName;
    }
    const std::string &ComponentHealth::GetName()
    {
        return componentName;
    }
    void ComponentHealth::load(const nlohmann::json &jsonData)
    {
        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentOnFire = GameSingleton::gameRegistry->get<ComponentOnFire *>(entity);

        ComponentPlayer **componentPlayerPtr = GameSingleton::gameRegistry->try_get<ComponentPlayer *>(entity);
        if (componentPlayerPtr != nullptr)
        {
            componentPlayer = *componentPlayerPtr;
        }
        else
        {
            componentPlayer = nullptr;
        }

        health = jsonData["Health"].get<float>();
        air = jsonData["Air"].get<float>();
        canStrand = jsonData["CanStrand"].get<bool>();
        airCapacity = jsonData["AirCapacity"].get<float>();
        attackResilience = jsonData["AttackResilience"].get<float>();
        fallResilience = jsonData["FallResilience"].get<float>();
        fireResilience = jsonData["FireResilience"].get<float>();
        corpseDuration = jsonData["CorpseDuration"].get<float>();
        deathTime = jsonData["DeathTime"].get<double>();
        breathingMode = (BreathingMode)jsonData["BreathingMode"].get<uint8_t>();
        invincibleTime = 2.5f;
    }
    void ComponentHealth::save(nlohmann::json &jsonData)
    {
        jsonData["Health"] = health;
        jsonData["Air"] = air;
        jsonData["DeathTime"] = deathTime;
    }
    void ComponentHealth::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "Health", "float", health);
        MSG_VALUE_SET(jsonData, "Air", "float", air);
        MSG_VALUE_SET(jsonData, "CorpseDuration", "float", corpseDuration);
    }
} // namespace PocketSurvival
